import time
import snap7
from snap7 import util


class PlcController(object):
    def __init__(self) -> None:
        self.client = None

    def connect(self, ip, rack=0, slot=1):
        """
        @brief Connects a Client Object to a PLC.

        @Args:
            ip: IP address of the PLC.
            rack: rack number where the PLC is located.
            slot: slot number where the CPU is located.
        """
        self.client = snap7.client.Client()
        self.client.set_connection_type(3)
        self.client.connect(ip, rack, slot)

    def disconnect(self):
        """
        @brief Disconnect a client.
        """
        self.client.disconnect()

    def get_bool(self, byte_array, bool_index) -> bool:
        """
        @brief get a bool value for byte_array.

        @Args:
            byte_array: byte_array to be get.
            bool_index: value of the byte_array to be get. 
        """
        value = util.get_bool(byte_array, 0, bool_index)
        return value


    def write(self, start, dbnum, index, value):
        """
        @brief Writes a data area into a PLC.

        @Args:
            start: byte index to start writting.
            dbnum: number of the db to be write to. 
            index: bit index to read from.
            value: bool to be write.
        """
        area = snap7.types.Areas.MK
        byte_array = self.client.read_area(area, dbnum, start, 1)
        time.sleep(0.01)
        util.set_bool(byte_array, 0, index, value)
        self.client.write_area(area, dbnum, start, byte_array)
        time.sleep(0.01)

    def read(self, start, dbnum, size):
        """
        @brief  Reads a data area from a PLC

        Args:
            dbnum: number of the db to be read from.
            start: byte index to start reading.
            size: number of bytes to read.

        Returns:
            Buffer with the data read.
        """
        area = snap7.types.Areas.MK
        byte_array = self.client.read_area(area, dbnum, start, size)
        return byte_array

    def destroy(self):
        """
        @brief  Destroys the Client object.
        """
        self.client.destroy()
